#include <linux/module.h>
#include <linux/init.h>
#include <linux/moduleparam.h>

#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/timer.h>
#include <linux/types.h>
#include <linux/jiffies.h>
#include <linux/errno.h>

#include <asm/atomic.h>
#include <asm/uaccess.h>








#define SECOND_MAJOR 0
static int second_major = SECOND_MAJOR;

struct second_dev {
	struct cdev cdev;
	atomic_t counter;
	struct timer_list s_timer;
};

struct second_dev *second_devp;
struct class *second_class;

static void second_timer_handle(unsigned long arg)
{
	mod_timer(&second_devp->s_timer,jiffies + HZ);
	atomic_inc(&second_devp->counter);

	printk(KERN_NOTICE "current jiffies is %ld\n", jiffies);
}

int second_open(struct inode *inode, struct file *filp)
{
	init_timer(&second_devp->s_timer);
	second_devp->s_timer.function = &second_timer_handle;
	second_devp->s_timer.expires = jiffies + HZ;

	add_timer(&second_devp->s_timer);
	atomic_set(&second_devp->counter, 0);

	return 0;
}

int second_release(struct inode *inode, struct file *filp)
{
	del_timer(&second_devp->s_timer);

	return 0;
}

static ssize_t second_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
	int counter;

	counter = atomic_read(&second_devp->counter);
	if (put_user(counter, (int *)buf))
		return -EFAULT;
	else
		return sizeof(unsigned int);
}

static const struct file_operations second_fops =
{
	.owner = THIS_MODULE,
	.open = second_open,
	.release = second_release,
	.read = second_read,
};

static void second_setup_cdev(struct second_dev *dev, dev_t devnum)
{
	int err;
	dev_t devno;
	
	devno = devnum;
	cdev_init(&dev->cdev, &second_fops);
	dev->cdev.owner = THIS_MODULE;
	dev->cdev.ops = &second_fops;
	err =cdev_add(&dev->cdev, devno, 1);
	if (err) 
		printk(KERN_NOTICE "Error %d adding second devices \n",err);
} 

static int second_init(void)
{
	int retval;
	dev_t devno;
	
	retval = alloc_chrdev_region(&devno, 0, 1, "second");
	second_major = MAJOR(devno); //for release function
	if (retval < 0)
		return retval;
	second_devp = kmalloc(sizeof(struct second_dev), GFP_KERNEL);
	if (!second_devp) {
		return -ENOMEM;
		goto fail_kmalloc;
	}

	memset(second_devp, 0, sizeof(struct second_dev));
	second_setup_cdev(second_devp, devno);
	
	second_class = class_create(THIS_MODULE, "second");
	if(IS_ERR(second_class)) {
        	printk("Error: failed in creating second class.\n");
		return 0;
	}
	device_create(second_class, NULL, devno, NULL,"second");

	return 0;
	
fail_kmalloc:
	unregister_chrdev_region(devno, 1);
}

static void second_cleanup(void)
{
	cdev_del(&second_devp->cdev);
	kfree(second_devp);
	unregister_chrdev_region(MKDEV(second_major, 0), 1);

	device_destroy(second_class, MKDEV(second_major, 0));
	class_destroy(second_class);
}

MODULE_AUTHOR("wangxy");
MODULE_LICENSE("GPL");

module_param(second_major, int, S_IRUGO);

module_init(second_init);
module_exit(second_cleanup);
